import VMBase from './VMBase';
import VMLabel from './VMLabel';

const { ccclass, property, executeInEditMode, menu } = cc._decorator;


/**
 * 自动检查识别的数组,你可以准备自己的组件放上去自动识别
 */
const COMP_ARRAY_CHECK = [
    ['BhvFrameIndex', 'index', false],
    ['BhvGroupToggle', 'index', false],
    ['BhvRollNumber', 'targetValue', false],
    //组件名、默认属性、controller值
    ['cc.Label', 'string', false],
    ['cc.RichText', 'string', false],
    ['cc.EditBox', 'string', true],
    ['cc.Slider', 'progress', true],
    ['cc.ProgressBar', 'progress', false],
    ['cc.Toggle', 'isChecked', true]
];

/**
 * 不能自动识别时 手动传入 初始化用的 map
 */
const Manual_CHECK_MAP = {
    //组件名、默认属性、controller值
    [fgui.GTextInput.name] : {componentProperty:'text',controller:true},
    [fgui.GButton.name] : {componentProperty:'selected',controller:true},
    [fgui.GComboBox.name] : {componentProperty:'value',controller:true},
    [fgui.GProgressBar.name] : {componentProperty:'value',controller:false},
    [fgui.GSlider.name] : {componentProperty:'value',controller:true},
}

/**
 * 自动初始化需要的参数 类
 */
class VMInitProperty {

    /**绑定到的节点 */
    bindNode: cc.Node;
    /**非label模板模式时 监听的数据源路径*/
    watchPath: string = "";
    /**是否开启label模板模式 */
    tempmode: boolean = false;
    /**label模板模式时 监听的数据源路径数组 */
    watchPathArr: string[] = [];
    /**部分 fgui组件 无法用node.getComponent获取 需要直接传入 */
    watchComponent: any;
    /**直接传入的fgui组件的类型 */
    watchComponentType: string;
    /**自定义的观察回调 */
    onValueChanged:any;

    /**
     * 默认构造函数
     * @param bindNode 绑定的节点 
     * @param watchPath 观察数据源路径
     */
    constructor(bindNode?:cc.Node,watchPath?:string){
        this.bindNode = bindNode;
        this.watchPath = watchPath;
    }

    /**
     * 构造 模板类型label
     * @param bindNode 绑定的节点
     * @param watchPathArr 观察数据源路径 数组
     */
    constructorTempmodeLabel(bindNode:cc.Node,watchPathArr:string[]){
        this.bindNode = bindNode;
        this.watchPathArr = watchPathArr;
        this.tempmode = true;
    }

    /**
     * 构造 无法getComponent获取的类型 如GTextInput
     * @param watchPath 观察数据源路径
     * @param watchComponent 手动绑定的组件
     * @param watchComponentType 组件type
     */
    constructorManualType(watchPath:string,watchComponent:any,watchComponentType:string,onValueChanged?:any){
        this.bindNode = watchComponent.node;
        this.watchPath = watchPath;
        this.watchComponent = watchComponent;
        this.watchComponentType = watchComponentType;
        this.onValueChanged = onValueChanged;
    }

}

/**
 * [VM-Custom]
 * 自定义数值监听, 可以快速对该节点上任意一个组件上的属性进行双向绑定
 */
@ccclass
@executeInEditMode
@menu('ModelViewer/VM-Custom (自定义VM)')
export default class VMCustom extends VMBase {
    /**
     * 默认构造函数
     * @param bindNode 绑定的节点 
     * @param watchPath 观察数据源路径
     */
    static nomalInit(bindNode:cc.Node,watchPath:string){
        let vmInitProperty = new VMInitProperty(bindNode,watchPath)
        this.autoInit(vmInitProperty)
    }

    /**
     * 构造 模板类型label
     * @param bindNode 绑定的节点
     * @param watchPathArr 观察数据源路径 数组
     */
    static tempmodeLabelInit(bindNode:cc.Node,watchPathArr:string[]){
        let vmInitProperty = new VMInitProperty()
        vmInitProperty.constructorTempmodeLabel(bindNode,watchPathArr)
        this.autoInit(vmInitProperty)
    }

    /**
     * 构造 无法getComponent获取的类型 如GTextInput
     * @param watchPath 观察数据源路径
     * @param watchComponent 手动绑定的组件
     * @param watchComponentType 组件type
     * @param onValueChanged 自定义的 观察者 回调
     */
    static manualTypeInit(watchPath:string,watchComponent:any,watchComponentType:string,onValueChanged?:any) {
        let vmInitProperty = new VMInitProperty()
        vmInitProperty.constructorManualType(watchPath,watchComponent,watchComponentType,onValueChanged)
        this.autoInit(vmInitProperty)
    }

    /**
     * 适配fgui 传入node节点与观察路径自动完成绑定
     */
    protected static autoInit(vmInitProperty: VMInitProperty) {
        vmInitProperty.bindNode.addComponent(VMCustom)
        let vmCustom = vmInitProperty.bindNode.getComponent(VMCustom)
        // 无法getComponent获取的类型 特殊初始化
        if (vmInitProperty.watchComponent) {
            vmCustom.watchPath = vmInitProperty.watchPath
            vmCustom._watchComponent = vmInitProperty.watchComponent
            vmCustom.componentName = vmInitProperty.watchComponentType
            let chekMapObj = Manual_CHECK_MAP[vmInitProperty.watchComponentType]
            if (chekMapObj) {
                vmCustom.componentProperty = chekMapObj.componentProperty
                vmCustom.controller = chekMapObj.controller
                vmCustom._canWatchComponent = vmCustom.controller
            }

            vmCustom.onEnableFgui()
        } else {
            //只在运行时检查组件是否缺失可用
            vmCustom.checkEditorComponent();//编辑器检查
            if (vmCustom.componentName == "cc.Label" && (vmInitProperty.watchPathArr.length > 1 || vmInitProperty.tempmode)) {
                // 模板模式时改为创建VMLabel组件
                vmInitProperty.bindNode.removeComponent(VMCustom)
                vmInitProperty.bindNode.addComponent(VMLabel)
                let vmLabel = vmInitProperty.bindNode.getComponent(VMLabel)
                vmLabel.templateMode = true
                vmLabel.setWatchPathArr(vmInitProperty.watchPathArr)
                vmLabel.onLoadFgui()
            } else if (vmInitProperty.watchPathArr.length == 1) {
                vmCustom.watchPath = vmInitProperty.watchPath
                vmCustom.onLoadFgui()
            }
        }

        //当有自定义观察回调时重写setComponentValue方法 (因onValueChanged重写失败)
        if(vmInitProperty.onValueChanged){
            vmCustom.setComponentValue = vmInitProperty.onValueChanged
        }
    }

    /******************** 以上是静态函数 ************************/

    @property({
        tooltip: '激活controller,以开启双向绑定，否则只能接收消息'
    })
    controller: boolean = false;

    @property
    watchPath: string = "";

    @property({
        tooltip: '绑定组件的名字'
    })
    componentName: string = "";

    @property({
        tooltip: '组件上需要监听的属性'
    })
    componentProperty: string = "";

    @property({
        tooltip: '刷新间隔频率(只影响脏检查的频率)',
        step: 0.01,
        range: [0, 1],
        visible: function () { return this.controller === true }
    })
    refreshRate: number = 0.1;


    //计时器
    private _timer = 0;

    /**监听的组件对象 */
    private _watchComponent: any = null;

    /**是否能监听组件的数据 */
    private _canWatchComponent: boolean = false;

    /**检查的值 */
    private _oldValue: any = null;


    // LIFE-CYCLE CALLBACKS:

    

    onLoadFgui() {
        super.onLoadFgui();
        if (!CC_EDITOR) {
            this._watchComponent = this.node.getComponent(this.componentName);
            this.checkComponentState();
        }

        super.onEnableFgui();
    }

    onRestore() {
        this.checkEditorComponent();
    }

    //挂在对应节点后，自动获取组件属性和名字
    checkEditorComponent() {
        // if (CC_EDITOR) {
        let checkArray = COMP_ARRAY_CHECK;
        this.controller = false;
        for (let i = 0; i < checkArray.length; i++) {
            const params = checkArray[i];
            let comp = this.node.getComponent(params[0] as string);
            if (comp) {
                if (this.componentName == '') this.componentName = params[0] as string;
                if (this.componentProperty == '') this.componentProperty = params[1] as string;
                if (params[2] !== null) this.controller = params[2] as boolean;

                break;
            }

        }
        // }


    }

    checkComponentState() {
        this._canWatchComponent = false;
        if (!this._watchComponent) { console.error('未设置需要监听的组件'); return; }
        if (!this.componentProperty) { console.error('未设置需要监听的组件 的属性'); return; }
        if (this.componentProperty in this._watchComponent === false) { console.error('需要监听的组件的属性不存在'); return; }
        this._canWatchComponent = true;
    }

    getComponentValue() {
        return this._watchComponent[this.componentProperty];

    }

    setComponentValue(value: any) {
        //如果遇到cc.Toggle 组件就调用上面的方法解决
        if (this.componentName == "cc.Toggle") {
            if (value == true) {
                this.node.getComponent(cc.Toggle).check();
            }
            if (value == false) {
                this.node.getComponent(cc.Toggle).uncheck();
            }
        } else {
            this._watchComponent[this.componentProperty] = value;
        }
    }


    /**初始化获取数据 */
    onValueInit() {
        if (CC_EDITOR) return; //编辑器模式不初始化
        //更新信息
        this.setComponentValue(this.VM.getValue(this.watchPath));
    }

    /**[可重写]组件的值发生变化后，触发更新此值 */
    onValueController(newValue, oldValue) {
        this.VM.setValue(this.watchPath, newValue);
    }

    /**[可重写]初始化改变数据 */
    onValueChanged(n, o, pathArr: string[]) {
        this.setComponentValue(n);
    }

    update(dt) {
        //脏检查（组件是否存在，是否被激活）
        if (CC_EDITOR == true) return;
        //if (this.templateMode == true) return; //todo 模板模式下不能计算  
        if (!this.controller) return;
        if (!this._canWatchComponent || this._watchComponent['enabled'] === false) return;

        //刷新频率检查
        this._timer += dt;
        if (this._timer < this.refreshRate) return;
        this._timer = 0;

        let oldValue = this._oldValue;
        let newValue = this.getComponentValue();

        if (this._oldValue === newValue) return;
        this._oldValue = this.getComponentValue();
        this.onValueController(newValue, oldValue);

    }
}
